package com.spark.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.spark.tags.SparkTag;
import com.spark.tags.XMLTag;

import freemarker.template.Configuration;

public class SparkConfiguration {

	public static final String DEFAULT_ENCODING = "UTF-8";
	public static final String CONTENT_TYPE_HTML = "text/html;charset=utf-8";
	public static final String CONTENT_TYPE_JSON = "application/json;charset=utf-8";
	public static final String X_REQUESTED_WITH = "X-Requested-With";
	public static final String XML_HTTP_REQUEST = "XMLHttpRequest";
	
	private static final String DEFAULT_CONFIGURATION = "spark.xml";	
	private static final String DEFAULT_FREEMARKER_TEMPLATES = "freemarker_templates";
	
	private static final String PROPERTITY_CONFIGURATION = "configuration";
	private static final String CONFIGURATION_NAME = "name";
	private static final String CONFIGURATION_VALUE = "value";
	
	private static final String PROPERTITY_TAGS = "tags";
	private static final String TAG_CLASS = "class";
	private static final String TAG_NAME = "name";
	
	private static final String PROPERTITY_ACTIONS = "actions";
	public static final String ACTION_NAME = "name";
	public static final String ACTION_CLASS = "class";
	public static final String ACTION_METHOD = "method";
	public static final String ACTION_CHECK_SESSION = "checkSession";
	public static final String ACTION_INSTANCE = "instance";
	
	private String context_path;
	private File configFile;
	private Configuration freemarkerConfiguration;
	private HashMap<String, String> configuration;
	private HashMap<String, Map<String, Object>> tagCache;
	private HashMap<String, Map<String, Object>> actionCache;
	private SparkTag defaultTag;
	
	private static SparkConfiguration instance = new SparkConfiguration();

	private SparkConfiguration(){};
	
	public static SparkConfiguration getInstance(){
		return instance;		
	}
	
//	public SparkConfiguration() throws Exception {
//		init(Thread.currentThread().getContextClassLoader().getResourceAsStream(DEFAULT_CONFIGURATION));
//	}
//
//	public SparkConfiguration(File dir, String fileName) throws Exception {
//		configFile = new File(dir, fileName);
//		init(new FileInputStream(configFile));
//	}

	public void init(String path) throws Exception {
		context_path = path;
		configFile = new File(new File(context_path), DEFAULT_CONFIGURATION);
		if (!configFile.exists()) throw new RuntimeException("Can not found " + DEFAULT_CONFIGURATION);
		init(new FileInputStream(configFile));
	}

	private void init(InputStream is) throws Exception {
		try {
			configuration = new HashMap<String, String>(100);
			tagCache = new HashMap<String, Map<String, Object>>(100);
			actionCache = new HashMap<String, Map<String, Object>>(100);
			XMap configMap = XMapParser.parseStream(is);
			XMap configurationMap = configMap.getChild(PROPERTITY_CONFIGURATION);
			initConfiguration(configurationMap);
			initFreemarkerConfiguration();
			XMap tags = configMap.getChild(PROPERTITY_TAGS);
			initTags(tags);
			XMap actions = configMap.getChild(PROPERTITY_ACTIONS);			
			initActions(actions);
		}finally{
			if(is!=null)is.close();
		}
	}

	private void initConfiguration(XMap map) {
		if (map == null)
			return;
		List<XMap> children = map.getChilds();
		if (children != null) {
			Iterator<XMap> it = children.iterator();
			while (it.hasNext()) {
				XMap item = it.next();
				configuration.put(item.getString(CONFIGURATION_NAME), item.getString(CONFIGURATION_VALUE));
			}
		}
	}
	
	public String getDefaultFreemarkerTemplates(){
		return configuration.get(DEFAULT_FREEMARKER_TEMPLATES);
	}
	
	private void initFreemarkerConfiguration() throws IOException{
		freemarkerConfiguration = new Configuration();
		freemarkerConfiguration.setTemplateUpdateDelay(0);//模板更新时间
		freemarkerConfiguration.setDefaultEncoding(DEFAULT_ENCODING);
		freemarkerConfiguration.setOutputEncoding(DEFAULT_ENCODING);
		freemarkerConfiguration.setNumberFormat("#");
		String templates = getDefaultFreemarkerTemplates();
		File templatesFile;
		if(configFile!=null){
			templatesFile = configFile.getParentFile();
//			templatesFile = new File(configFile.getParentFile(),templates);
		}else{
			templatesFile = new File(templates);
		}
		freemarkerConfiguration.setDirectoryForTemplateLoading(templatesFile);
	}

	private void initTags(XMap tags) throws Exception {
		List<XMap> children = tags.getChilds();
		Map<String, Object> clazzMap = null;
		if (children != null) {
			Iterator<XMap> it = children.iterator();
			while (it.hasNext()) {
				XMap tagMap = it.next();
				String nameSpace = tagMap.getNamespaceURI();
				if (!"".equals(nameSpace)) {
					clazzMap = tagCache.get(nameSpace);
					if (clazzMap == null) {
						clazzMap = new HashMap<String, Object>(100);
						tagCache.put(nameSpace, clazzMap);
					}
				}
				String clazz = tagMap.getString(TAG_CLASS);
				Object tag = Class.forName(clazz).newInstance();
				String name = tagMap.getString(TAG_NAME);
				clazzMap.put(name, tag);
			}
		}
	}
	
	
	private void initActions(XMap actions) throws Exception{
		List<XMap> children = actions.getChilds();
		List<String> names = new ArrayList<String>();
		Map<String,Object> instances = new HashMap<String,Object>(100);
		if (children != null) {
			Iterator<XMap> it = children.iterator();
			while (it.hasNext()) {
				XMap action = it.next();
				String name = action.getString(ACTION_NAME);
				String method = action.getString(ACTION_METHOD);
				String checkSession = action.getString(ACTION_CHECK_SESSION);
				String clazz = action.getString(ACTION_CLASS);
				if(name==null||"".equals(name)||method==null||"".equals(method)||clazz==null||"".equals(clazz))
					throw new Exception("action配置不正确!");
				if(names.contains(name)) throw new Exception("存在相同的action name : " + name);
				names.add(name);
				Map<String, Object> map = actionCache.get(name);
				if (map == null) {
					map = new HashMap<String, Object>(100);
					actionCache.put(name, map);
				}
				Object actObj = instances.get(clazz);
				if(instances.get(clazz)==null){
					actObj = Class.forName(clazz).newInstance();
					instances.put(clazz, actObj);
				}
				map.put(ACTION_CHECK_SESSION, checkSession);
				map.put(ACTION_METHOD, method);
				map.put(ACTION_INSTANCE, actObj);
			}
		}
	}

	public SparkTag getTag(String nameSpace, String name) {
		Map<String, Object> tagMap = tagCache.get(nameSpace);
		if (tagMap != null) {
			return (SparkTag)tagMap.get(name);
		}
		return getDefaultTag();
	}
	
	public Object getObject(String nameSpace, String name) {
		Map<String, Object> tagMap = tagCache.get(nameSpace);
		if (tagMap != null) {
			return tagMap.get(name);
		}
		return null;
	}
	
	public Object getActionInstance(String action){
		Map<String, Object> actionMap = actionCache.get(action);
		if (actionMap != null) {
			return actionMap.get(ACTION_INSTANCE);
		}
		return null;
	}
	
	public String getActionMethod(String action){
		Map<String, Object> actionMap = actionCache.get(action);
		if (actionMap != null) {
			return (String)actionMap.get(ACTION_METHOD);
		}
		return null;
	}
	
	public boolean isCheckActionSession(String action){
		Map<String, Object> actionMap = actionCache.get(action);
		boolean isCheckSession = true;
		if (actionMap != null) {
			String checkSession = (String)actionMap.get(ACTION_CHECK_SESSION);
			if(checkSession!=null){
				isCheckSession = Boolean.valueOf(checkSession);
			}
		}
		return isCheckSession;
	}
	
	private SparkTag getDefaultTag(){
		if(defaultTag==null){
			defaultTag = new XMLTag();
		}
		return defaultTag;
	}
	
//	public SparkContext createContext(){
//		SparkContext context = new SparkContext(this);
//		return context;
//	}
	
	public String getContextPath(){
		return context_path;
	}

	public Configuration getFreemarkerConfiguration() {
		return freemarkerConfiguration;
	}

}
